import {
  Clazz,
  BeanDefine,
  MethodDefine,
  FieldDefine,
  BaseDefine,
} from './SpringType';

/**
 * 字段注解缓存
 * 场景：继承多态
 * 例如
 *  abstract A {
 *   @Autowired()
 *   name:string
 *  }
 *
 *  class A1 extends A{};
 *  class A2 extends A{};
 *
 *  但是只有A1存在name字段注解，这是因为这个Autowired注解只会在扫描A1的时候执行一次。先扫描Autowired然后扫描A1，一个类扫描结束。然后扫描A2，就会缺少属性。
 *  解决办法：
 *  这里我们把Autowired注解信息缓存起来 {target,name,annotation} 这里的target就是A。在copy前阶段判断A2和target的关系，重新添加注解！
 *
 */
type FiledDefineCache = {
  target: Clazz;
  fieldDefine: FieldDefine;
};
type MethodDefineCache = {
  target: Clazz;
  methodDefine: MethodDefine;
};

//字段缓存
const fieldDefineCache: FiledDefineCache[] = [];
//方法缓存
const methodDefineCache: MethodDefineCache[] = [];

export function addFiledDefineToCache(f: FiledDefineCache) {
  fieldDefineCache.push(f);
}
export function addMethodDefineToCache(f: MethodDefineCache) {
  methodDefineCache.push(f);
}

//扩展未继承上的定义
export function addExtendDefineInfo(
  bean: any,
  bd: BeanDefine,
  log: (a: string) => void
) {
  log(`beanDefine提升阶段,追加父级未继承的属性`);

  //过滤器
  const filterFn = (f: { target: Clazz }) =>
    bd.clazz != f.target && instanceOf(bean, f.target);

  //追加父级的字段
  const fdList = fieldDefineCache.filter(filterFn);
  //追加父级的方法
  const mdList = methodDefineCache.filter(filterFn);

  const showAnnotaionInfo = (bd: BaseDefine) =>
    bd.annotationList.map(a => '@' + a.clazz.name).join(',');

  if (fdList.length > 0) {
    log(`${bd.clazz.name}添加父类注解字段,数量:${fdList.length}`);
    fdList.forEach(f => {
      const hasDefine = bd.fieldList.find(v => v.name == f.fieldDefine.name);
      if (!hasDefine) {
        bd.fieldList.push(f.fieldDefine);
        log(
          `添加注解字段:${f.fieldDefine.name} [ ${showAnnotaionInfo(
            f.fieldDefine
          )} ] <= ${f.target.name}`
        );
      } else {
        log(`字段:${hasDefine.name} 已存在`);
      }
    });
  }

  if (mdList.length > 0) {
    log(`${bd.clazz.name}添加父类注解方法,数量:${mdList.length}`);
    mdList.forEach(f => {
      const hasDefine = bd.methodList.find(v => v.name == f.methodDefine.name);
      if (!hasDefine) {
        bd.methodList.push(f.methodDefine);
        log(
          `添加注解方法:${f.methodDefine.name} [${showAnnotaionInfo(
            f.methodDefine
          )}] <= ${f.target.name}`
        );
      } else {
        log(`方法:${hasDefine.name} 已存在`);
      }
    });
  }
}

/**
 * 实例化判断
 * 判断一个bean对象是否继承于targetClass
 *
 */
export function instanceOf(bean: any, targetClass: Clazz): boolean {
  if (bean.constructor === targetClass) return true;

  const parentContructor = Object.getPrototypeOf(bean.constructor);

  if (!parentContructor.name) return false;

  return parentContructor === targetClass
    ? true
    : instanceOf(new parentContructor(), targetClass);
}
